/*
  +----------------------------------------------------------------------+
  | PHP Version 7                                                        |
  +----------------------------------------------------------------------+
  | Copyright (c) 1997-2016 The PHP Group                                |
  +----------------------------------------------------------------------+
  | This source file is subject to version 3.01 of the PHP license,      |
  | that is bundled with this package in the file LICENSE, and is        |
  | available through the world-wide-web at the following url:           |
  | http://www.php.net/license/3_01.txt                                  |
  | If you did not receive a copy of the PHP license and are unable to   |
  | obtain it through the world-wide-web, please send a note to          |
  | license@php.net so we can mail you a copy immediately.               |
  +----------------------------------------------------------------------+
  | Author:                                                              |
  +----------------------------------------------------------------------+
*/

/* $Id$ */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_demo.h"

/* If you declare any globals in php_demo.h uncomment this:
ZEND_DECLARE_MODULE_GLOBALS(demo)
*/

/* True global resources - no need for thread safety here */
static int le_demo;

/* {{{ PHP_INI
 */
/* Remove comments and fill if you need to have entries in php.ini
PHP_INI_BEGIN()
    STD_PHP_INI_ENTRY("demo.global_value",      "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_demo_globals, demo_globals)
    STD_PHP_INI_ENTRY("demo.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_demo_globals, demo_globals)
PHP_INI_END()
*/
/* }}} */

/* Remove the following function when you have successfully modified config.m4
   so that your module can be compiled into PHP, it exists only for testing
   purposes. */

/* Every user-visible function in PHP should document itself in the source */
/* {{{ proto string confirm_demo_compiled(string arg)
   Return a string to confirm that the module is compiled in */
PHP_FUNCTION(confirm_demo_compiled)
{
	char *arg = NULL;
	size_t arg_len, len;
	zend_string *strg;

	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg, &arg_len) == FAILURE) {
		return;
	}

	strg = strpprintf(0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "demo", arg);

	RETURN_STR(strg);
}
/* }}} */
/* The previous line is meant for vim and emacs, so it can correctly fold and
   unfold functions in source code. See the corresponding marks just before
   function definition, where the functions purpose is also documented. Please
   follow this convention for the convenience of others editing your code.
*/


/* {{{ php_demo_init_globals
 */
/* Uncomment this function if you have INI entries
static void php_demo_init_globals(zend_demo_globals *demo_globals)
{
	demo_globals->global_value = 0;
	demo_globals->global_string = NULL;
}
*/
/* }}} */

/* {{{ PHP_MINIT_FUNCTION
 */
PHP_MINIT_FUNCTION(demo)
{
	/* If you have INI entries, uncomment these lines
	REGISTER_INI_ENTRIES();
	*/
	return SUCCESS;
}
/* }}} */

/* {{{ PHP_MSHUTDOWN_FUNCTION
 */
PHP_MSHUTDOWN_FUNCTION(demo)
{
	/* uncomment this line if you have INI entries
	UNREGISTER_INI_ENTRIES();
	*/
	return SUCCESS;
}
/* }}} */

/* Remove if there's nothing to do at request start */
/* {{{ PHP_RINIT_FUNCTION
 */
PHP_RINIT_FUNCTION(demo)
{
#if defined(COMPILE_DL_DEMO) && defined(ZTS)
	ZEND_TSRMLS_CACHE_UPDATE();
#endif
	return SUCCESS;
}
/* }}} */

/* Remove if there's nothing to do at request end */
/* {{{ PHP_RSHUTDOWN_FUNCTION
 */
PHP_RSHUTDOWN_FUNCTION(demo)
{
	return SUCCESS;
}
/* }}} */

/* {{{ PHP_MINFO_FUNCTION
 */
PHP_MINFO_FUNCTION(demo)
{
	php_info_print_table_start();
	php_info_print_table_header(2, "demo support", "enabled");
	php_info_print_table_end();

	/* Remove comments if you have entries in php.ini
	DISPLAY_INI_ENTRIES();
	*/
}
/* }}} */

/* {{{ demo_functions[]
 *
 * Every user visible function must have an entry in demo_functions[].
 */
const zend_function_entry demo_functions[] = {
	PHP_FE(confirm_demo_compiled,	NULL)		/* For testing, remove later. */
        PHP_FE(demo_default,    NULL)
        PHP_FE(demo_string_connect, NULL)
        PHP_FE(demo_array_to_string,    NULL)
        PHP_FE(demo_array_to_string_2,  NULL)
        PHP_FE(demo_callback_func,  NULL)
        PHP_FE(demo_sign_make,  NULL)
        PHP_FE(demo_sign_verify,  NULL)
	PHP_FE_END	/* Must be the last line in demo_functions[] */
};
/* }}} */

/* {{{ demo_module_entry
 */
zend_module_entry demo_module_entry = {
	STANDARD_MODULE_HEADER,
	"demo",
	demo_functions,
	PHP_MINIT(demo),
	PHP_MSHUTDOWN(demo),
	PHP_RINIT(demo),		/* Replace with NULL if there's nothing to do at request start */
	PHP_RSHUTDOWN(demo),	/* Replace with NULL if there's nothing to do at request end */
	PHP_MINFO(demo),
	PHP_DEMO_VERSION,
	STANDARD_MODULE_PROPERTIES
};
/* }}} */

#ifdef COMPILE_DL_DEMO
#ifdef ZTS
ZEND_TSRMLS_CACHE_DEFINE()
#endif
ZEND_GET_MODULE(demo)
#endif

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: noet sw=4 ts=4 fdm=marker
 * vim<600: noet sw=4 ts=4
 */

void printType(zval arg){
    switch(Z_TYPE(arg)){
        case IS_NULL:
            php_printf("IS_NULL");
            break;
        case IS_TRUE:
                php_printf("IS_TRUE");
            break;
        case IS_FALSE:
                php_printf("IS_FALSE");
            break;
        case IS_LONG:
                php_printf("IS_LONG");
            break;
        case IS_DOUBLE:
                php_printf("IS_DOUBLE");
            break;
        case IS_STRING:
                php_printf("IS_STRING");
            break;
        case IS_OBJECT:
                php_printf("IS_OBJECT");
            break;
        case IS_RESOURCE:
                php_printf("IS_RESOURCE");
            break;
    }
}

void printType_P(zval *arg){
    switch(Z_TYPE_P(arg)){
        case IS_NULL:
            php_printf("IS_NULL");
            break;
        case IS_TRUE:
                php_printf("IS_TRUE");
            break;
        case IS_FALSE:
                php_printf("IS_FALSE");
            break;
        case IS_LONG:
                php_printf("IS_LONG");
            break;
        case IS_DOUBLE:
                php_printf("IS_DOUBLE");
            break;
        case IS_STRING:
                php_printf("IS_STRING");
            break;
        case IS_OBJECT:
                php_printf("IS_OBJECT");
            break;
        case IS_RESOURCE:
                php_printf("IS_RESOURCE");
            break;
    }
}

/*
 * 函数实现
 */

/**
 * 返回脚本传递的参数
 * @param string arg        参数内容
 * @example demo_default("Hello World!");
 * @return string
 */
PHP_FUNCTION(demo_default)
{   
    // 定义脚本参数
    char *arg = NULL;
    zend_string *result;        // 返回变量
    
    // 参数字节大写
    size_t arg_len;
    
    // 解析脚本参数
    if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE){
        return ;
    }
    
    // 脚本输出参数
    //php_printf("%s", arg);
    
    // 产生一个字符串：zend_string
    result = strpprintf(0, "%s", arg);
    
    // 返回字符串
    RETURN_STR(result);
}

/*
 * 字符串连接
 * */
PHP_FUNCTION(demo_string_connect){
    // 临时存放变量
    zval tmp;
    
    // 连接结果变量
    zval result;
    
    // 设置 tmp 为string类型，值为：Hello
    ZVAL_STRINGL(&tmp, "字符串", sizeof("字符串") - 1);
    
    // 连接到结果
    concat_function(&result, &result, &tmp);
    
    // 设置 tmp 为string类型；值为：C/C++
    ZVAL_STRINGL(&tmp, " 连接", sizeof(" 连接")-1);
    // 连接到 result
    concat_function(&result, &result, &tmp);
    
    // 释放变量内存
    zval_ptr_dtor(&tmp); 
    
    // 输出结果
    php_printf("%s", Z_STRVAL(result));
}

/*
 * 一维数组值转字符串 (宏方式遍历)
 * @param array arr         一维数组
 * @param sting flag        字符串连接标志
 * @return string
 */
PHP_FUNCTION(demo_array_to_string)
{
    // 定义脚本参数变量
    zval *arr;              // 一维数组
    char *flag = "&";      // 连接标志符
    size_t flag_len;        // 标识符字节长度
    
    // 内部变量定义
    HashTable *arr_hash;    // 数组 HashTable 
    ulong key_num;          // 数组元素个数；无符号长整型；宏：ulong = unsigned long
    zend_string *arr_key;   // 数组键；字符串类型；宏：zend_string = char *
    zend_string *tmp_val;   // 临时键值组合；key=val+flag; 如：name=jirenyou&
    zval *val;              // 数组值；任意类型；宏：zval = (s、l、d ...)
    zval tmp_str;           // 拼接临时字符串
    zval result;            // 拼接结果
    zend_string *return_str;    // 返回值
    
    // 获取脚本参数
    if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|s", &arr, &flag, &flag_len) == FAILURE){
        return ;
    }
    
    // 获取 arr 数组的 HashTable
    arr_hash = Z_ARRVAL_P(arr);
    
    // 键值key=>val遍历，拼接数组值
    ZEND_HASH_FOREACH_KEY_VAL(arr_hash, key_num, arr_key, val){
        // 数组、对象、资源不参与转换
        if(Z_TYPE_P(val) == IS_ARRAY || Z_TYPE_P(val) == IS_OBJECT || Z_TYPE_P(val) == IS_RESOURCE){
            continue;
        }
        
        // 将值转换为字符串
        convert_to_string(val);
        
//        // 输出数组键
//        php_printf("%s<br/>", arr_key->val);
//        // 输出数组值
//        php_printf("%s<br/>", Z_STRVAL_P(val));
//        // 输出连接符
//        php_printf("%s<br/>", flag);
        
        // 参数单个键值字符串；key=val&
        tmp_val = strpprintf(0, "%s=%s%s", arr_key->val, Z_STRVAL_P(val), flag);
        
        // 设置变量 tmp_str;为zend_string类型，值为：test
        //ZVAL_STRINGL(&tmp_str, "test", sizeof("test")-1);
        ZVAL_STRING(&tmp_str, tmp_val->val);
        // 拼接字符串到 result
        concat_function(&result, &result, &tmp_str);
        
    }ZEND_HASH_FOREACH_END();
    
    // 过滤最后一个连接标志
    ZVAL_STRINGL(&result, Z_STRVAL(result), Z_STRLEN(result)-strlen(flag));
    
    // 产生一个字符串
    return_str = strpprintf(0, "%s", Z_STRVAL(result));
    
    // 释放zval变量 tmp_str 和 result 的内存
    zval_ptr_dtor(&tmp_str);
    zval_ptr_dtor(&result);
    
    // 返回结果
    RETURN_STR(return_str);
}

/*
 * 一维数组值转字符串 (宏方式遍历)
 * @param array arr         一维数组
 * @param sting flag        字符串连接标志
 * @return string
 */
PHP_FUNCTION(demo_array_to_string_2)
{
    // 定义脚本参数变量
    zval *arr;              // 一维数组
    char *flag = NULL;      // 连接标志符
    size_t flag_len;        // 标识符字节长度
    
    // 内部变量定义
    HashTable *arr_hash;    // 数组 HashTable 
    ulong key_num;          // 数组元素个数；无符号长整型；宏：ulong = unsigned long
    zend_string *arr_key;   // 数组键；字符串类型；宏：zend_string = char *
    zend_string *tmp_val;   // 临时键值组合；key=val+flag; 如：name=jirenyou&
    zval *val;              // 数组值；任意类型；宏：zval = (s、l、d ...)
    zval tmp_str;           // 拼接临时字符串
    zval result;            // 拼接结果
    
    // 获取脚本参数
    if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "as", &arr, &flag, &flag_len) == FAILURE){
        return ;
    }
    
    // 获取 arr 数组的 HashTable
    arr_hash = Z_ARRVAL_P(arr);
    
    // 键值key=>val遍历，拼接数组值
    ZEND_HASH_FOREACH_KEY_VAL(arr_hash, key_num, arr_key, val){
        // 数组、对象、资源不参与转换
        if(Z_TYPE_P(val) == IS_ARRAY || Z_TYPE_P(val) == IS_OBJECT || Z_TYPE_P(val) == IS_RESOURCE){
            continue;
        }
        
        // 将值转换为字符串
        convert_to_string(val);
        
//        // 输出数组键
//        php_printf("%s<br/>", arr_key->val);
//        // 输出数组值
//        php_printf("%s<br/>", Z_STRVAL_P(val));
//        // 输出连接符
//        php_printf("%s<br/>", flag);
        
        // 参数单个键值字符串；key=val&
        tmp_val = strpprintf(0, "%s=%s%s", arr_key->val, Z_STRVAL_P(val), flag);
        
        // 设置变量 tmp_str;为zend_string类型，值为：test
        //ZVAL_STRINGL(&tmp_str, "test", sizeof("test")-1);
        ZVAL_STRING(&tmp_str, tmp_val->val);
        // 拼接字符串到 result
        concat_function(&result, &result, &tmp_str);
        
    }ZEND_HASH_FOREACH_END();
    
    // 过滤最后一个连接标志
    ZVAL_STRINGL(&result, Z_STRVAL(result), Z_STRLEN(result)-strlen(flag));
    
    // 释放变量 tmp_str 内存
    zval_ptr_dtor(&tmp_str);
    
    // 返回结果
    RETURN_STRING(Z_STRVAL(result));
}

/**
 * 回调PHP函数
 * @param string function_name      函数名
 * @param array params              参数列表
 * @return function_return
 */
PHP_FUNCTION(demo_callback_func){
    // 脚本参数变量
    zval* function_name;        // 接收函数名
    zval* arg=NULL;             // 函数参数
    
    // 内部变量
    ulong arg_num=1;            // 参数个数
    zval retval;                // 函数返回值
    
    //zval func_name;                   // 定义函数名
    //zval func_arg;                    // 定义函数参数
    //ZVAL_STRING(&func_name, "md5");   // 设置函数名
    //ZVAL_STRING(&func_arg, "123");    // 设置函数参数
    
    // 获取并解析脚本参数
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &function_name, &arg) == FAILURE)
    {
        RETURN_NULL();
    }
    
    // 检测函数名
    if (Z_TYPE_P(function_name) != IS_STRING)
    {
        php_printf("function name must be a string\n");
        RETURN_FALSE;
    }
    
    /**
     * 调用函数
     * @param EG(function_table)
     * @param NULL
     * @param string function_name      函数名
     * @param zval* retval              返回值
     * @param int 1                     参数格式
     * @param zval                      函数参数
     * @return 
     */
    // 设置参数
    //zval args[1] = {func_arg};
    zval args[1] = {*arg};
    //if(call_user_function(EG(function_table), NULL, &func_name, &retval, arg_num, args) != SUCCESS){       // 内部定义函数名调用
    if(call_user_function(EG(function_table), NULL, function_name, &retval, arg_num, args) != SUCCESS){         // 外部接收函数名调用
        php_printf("function callback failure!\n");
        RETURN_FALSE;
    }
    
    // 返回结果
    *return_value=retval;           // 设置返回值
    zval_copy_ctor(return_value);   // 拷贝一份值
    zval_ptr_dtor(&retval);         // 释放retval内存
    zval_ptr_dtor(arg);             // 释放变量arg内存
}

/**
 * 数据签名
 * @param array data            要签名的数据
 * @param string token          签名密钥
 * @return string
 */
PHP_FUNCTION(demo_sign_make){
    // 定义脚本参数变量
    zval *arr;              // 一维数组
    char *token = "123456ABCDEFGHIJKLMNOPQRSTUVWXYZ";      // 签名密钥
    size_t token_len;       // 密钥字节长度
    
    // 内部变量定义
    HashTable *arr_hash;    // 数组 HashTable 
    ulong key_num;          // 数组元素个数；无符号长整型；宏：ulong = unsigned long
    zend_string *arr_key;   // 数组键；字符串类型；宏：zend_string = char *
    zval *val;              // 数组值；任意类型；宏：zval = (s、l、d ...)
    zend_string *tmp_val;   // 临时键值组合；key=val+flag; 如：name=jirenyou&
    zval tmp_str;           // 拼接临时字符串
    zval result;            // 拼接结果
    zend_string *return_str;    // 返回值
    
    // 获取脚本参数
    if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|s", &arr, &token, &token_len) == FAILURE){
        return ;
    }

    /******************************* 数组排序 START *********************************************/
//    zval func_ksort;                      // 定义函数名变量
//    ZVAL_STRING(&func_ksort, "ksort");      // 函数名变量赋值
//    
//    zval func_ksort_arg;                  // 定义函数参数变量
//    ZVAL_STRING(&func_ksort_arg, "44");        // 参数赋值
//    //zval ksort_args[1] = {func_ksort_arg};      // 设置ksort函数参数
//    zval ksort_args[1] = {*arr};      // 设置ksort函数参数
//    
//    zval func_ksort_ret;      // 定义函数返回值变量
//    
//    // 调用函数
//    //if(call_user_function(EG(function_table), NULL, &func_ksort, &func_ksort_ret, 1, ksort_args) != SUCCESS){
//    if(call_user_function(EG(function_table), NULL, &func_ksort, &func_ksort_ret, 1, ksort_args) != SUCCESS){
//        php_printf("ksort function callback failure!\n");
//        RETURN_FALSE;
//    }
//    
//    php_var_dump(ksort_args, 1 TSRMLS_CC);
    
    
    // 返回结果
    //*return_value=func_ksort_ret;           // 设置返回值
//    zval_copy_ctor(return_value);   // 拷贝一份值
//    zval_ptr_dtor(&func_ksort_ret);         // 释放retval内存
    //zval_ptr_dtor(&func_ksort_arg);             // 释放变量arg内存
    //zval_ptr_dtor(ksort_args);             // 释放变量arg内存
    
    /******************************* 数组排序 END *********************************************/
    
    // 获取 arr 数组的 HashTable
    arr_hash = Z_ARRVAL_P(arr);
    
    // 键值key=>val遍历，拼接数组值
    ZEND_HASH_FOREACH_KEY_VAL(arr_hash, key_num, arr_key, val){
        // 数组、对象、资源不参与转换
        if(Z_TYPE_P(val) == IS_ARRAY || Z_TYPE_P(val) == IS_OBJECT || Z_TYPE_P(val) == IS_RESOURCE){
            continue;
        }
        
        // 将值转换为字符串
        convert_to_string(val);
        
        // 参数单个键值字符串；key=val&
        tmp_val = strpprintf(0, "%s=%s&", arr_key->val, Z_STRVAL_P(val));
        
        // 设置变量 tmp_str;为zend_string类型，值为：test
        ZVAL_STRING(&tmp_str, tmp_val->val);
        // 拼接字符串到 result
        concat_function(&result, &result, &tmp_str);
        
    }ZEND_HASH_FOREACH_END();
    
    // 连接 token 密钥
    tmp_val = strpprintf(0, "%s=%s", "sign_token", token);
    ZVAL_STRING(&tmp_str, tmp_val->val);
    concat_function(&result, &result, &tmp_str);
    
    // 释放zval变量 tmp_str 和 result 的内存
    zval_ptr_dtor(&tmp_str);
    zval_ptr_dtor(&result);
    
    /******************************* MD5 加密 START *********************************************/
    
    zval func_md5;                      // 定义函数名变量
    ZVAL_STRING(&func_md5, "md5");      // 函数名变量赋值
    
    zval func_md5_arg;                  // 定义函数参数变量
    ZVAL_STRING(&func_md5_arg, Z_STRVAL(result));        // 参数赋值
    zval md5_args[1] = {func_md5_arg};      // 设置md5函数参数
    
    zval func_md5_ret;      // 定义函数返回值变量
    
    // 调用函数
    if(call_user_function(EG(function_table), NULL, &func_md5, &func_md5_ret, 1, md5_args) != SUCCESS){
        php_printf("md5 function callback failure!\n");
        RETURN_FALSE;
    }
    
    // 返回结果
    *return_value=func_md5_ret;           // 设置返回值
    zval_copy_ctor(return_value);   // 拷贝一份值
    zval_ptr_dtor(&func_md5_ret);         // 释放retval内存
    zval_ptr_dtor(&func_md5_arg);             // 释放变量arg内存
    zval_ptr_dtor(md5_args);             // 释放变量arg内存
    
    /******************************* MD5 加密 END *********************************************/
    
    

//    // 产生一个字符串
//    return_str = strpprintf(0, "%s", Z_STRVAL(result));
//    // 返回结果
//    RETURN_STR(return_str);
}



PHP_FUNCTION(demo_sign_verify){
    zval *function_name;
    zval retval;
    
    zval *args;  
    
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &function_name, &args) == FAILURE) {
        return;
    }
    if (Z_TYPE_P(function_name) != IS_STRING) {
        php_printf("Function require string argumnets!");
        return;
    }
    
//    zval**params=(zval**)malloc(sizeof(zval));
//    params[0]="a";
    //TSRMLS_FETCH();
    if (call_user_function_ex(EG(function_table), NULL, function_name, &retval, 0, NULL, 0, NULL TSRMLS_CC) != SUCCESS) {
        php_printf("Function call failed!");
        return;
    }

    *return_value = retval;
    zval_copy_ctor(return_value);
    zval_ptr_dtor(&retval);
    
    
    
//    zval *function_name;
//    zval retval;
//    
//    zval *arr;
//    
//    
//    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &function_name) == FAILURE) {
//        return;
//    }
//    if (Z_TYPE_P(function_name) != IS_STRING) {
//        php_printf("Function require string argumnets!");
//        return;
//    }
//    //TSRMLS_FETCH();
//    if (call_user_function_ex(EG(function_table), NULL, function_name, &retval, 0, NULL, 0, NULL TSRMLS_CC) != SUCCESS) {
//        php_printf("Function call failed!");
//        return;
//    }
//
//    *return_value = retval;
//    zval_copy_ctor(return_value);
//    zval_ptr_dtor(&retval);

}